package com.gitee.l0km.codegen.webclient;

import static com.gitee.l0km.com4j.basex.bean.jdk.BeanPropertySupportImpl.BEAN_SUPPORT;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.namespace.QName;

import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.java2wsdl.TypeTable;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.l0km.codegen.base.AbstractSchema;
import com.gitee.l0km.codegen.base.CodeGenUtils;
import com.gitee.l0km.codegen.wsdl.WebServiceInfo;
import com.gitee.l0km.com4j.base.Assert;

/**
 * @author guyadong
 *
 */
public class ComplexType extends AbstractSchema implements KSoapConstants {
	private static final Logger logger = LoggerFactory.getLogger(ComplexType.class);

	private final ArrayList<XmlSchemaElement> fieldElements;
	private final XmlSchemaObject schemaObj;
	private final WebServiceInfo webServiceInfo;
	private final Object objectForPort;
	private final TypeTable typeTable;
	private KSoapConstants.TypePurpose purpose;
	private boolean used=false;
	private final AxisOperation operation;
	protected ComplexType(XmlSchemaObject schemaObj, WebServiceInfo webServiceInfo, Object objectForPort, AxisOperation operation) {
		Assert.notNull(schemaObj, "schemaObj");
		Assert.notNull(webServiceInfo, "webServiceInfo");
		this.objectForPort = objectForPort;
		this.schemaObj = schemaObj;
		this.webServiceInfo = webServiceInfo;
		this.typeTable = webServiceInfo.getAxisService().getTypeTable();
		this.operation=operation;
		if(schemaObj instanceof XmlSchemaComplexType){
			this.fieldElements = webServiceInfo.getFieldElements(((XmlSchemaComplexType)this.schemaObj).getQName());
		}else if(schemaObj instanceof XmlSchemaElement){
			this.fieldElements = webServiceInfo.getFieldElements(((XmlSchemaElement)schemaObj).getQName());
		}else 
			throw new IllegalArgumentException(String.format("invalid type,schemaObj must be  %s OR %s",
					XmlSchemaElement.class.getSimpleName(),XmlSchemaComplexType.class.getSimpleName()));	
		init();
	}

	private void addImportedBeanFieldClass(Class<?> beanClass) {
		this.addImportedClass(getReadablePropertiesReturnClass(beanClass).values().toArray(new Type[0]));
	}

	private void addImportedFieldElementsClass() {
		XmlSchemaElement element;
		String className;
		QName typeName;
		for (Iterator<XmlSchemaElement> it = getFieldElements().iterator(); it.hasNext();) {
			element = it.next();
			typeName = element.getSchemaTypeName();
			try {
				className = typeTable.getClassNameForQName(typeName);
				if(className!=null){
					this.addImportedClass(className);
				}
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
	}
	private void init(){
		try {
			//根据objectForPort的类型判断对象类型
			if(schemaObj instanceof XmlSchemaComplexType){
				String className = typeTable.getClassNameForQName(((XmlSchemaComplexType)this.schemaObj).getQName());
				if (className != null) {// bean对象
					this.baseClass = CodeGenUtils.forName(className);
					this.addImportedClass(baseClass);
					this.addImportedBeanFieldClass(baseClass);
					this.purpose = TypePurpose.BEAN;	
				}else{
					this.addImportedFieldElementsClass();
					this.purpose = TypePurpose.OTHER;
				}
			} else if (this.objectForPort.getClass().isArray()) {// request:参数类型数组(message)
				this.addImportedClass((Type[]) objectForPort);
				this.purpose = TypePurpose.REQUEST;
			} else if (this.objectForPort instanceof Type) {// response:返回类型(message)
				this.addImportedClass((Type) this.objectForPort);
				this.purpose = TypePurpose.RESPONSE;
			} else if (this.objectForPort==FAULT_MESSAGE) {// fault:异常返回消息(message)
				this.purpose = TypePurpose.FAULT;
			} else {
				// 其他消息类型
				this.addImportedFieldElementsClass();
				this.purpose = TypePurpose.OTHER;
			}
		} catch (ClassNotFoundException e) {
			logger.error(e.toString());
		} finally {
		}
	}
	@Override
	public boolean compile() {
		return true;
	}

	/**
	 * @return fieldElements
	 */
	public ArrayList<XmlSchemaElement> getFieldElements() {
		return fieldElements;
	}

	public Map<String, PropertyDescriptor> getReadableProperties() {
		return BEAN_SUPPORT.getProperties(this.baseClass, 2);
	}

	public Map<String, PropertyDescriptor> getRwProperties() {
		return BEAN_SUPPORT.getProperties(this.baseClass, 3);
	}

	public Map<String, Type> getReadablePropertiesReturnClass() {
		return getReadablePropertiesReturnClass(this.baseClass);
	}

	public Map<String, Type> getReadablePropertiesReturnClass(Class<?> beanClass) {
		try {
			Map<String, PropertyDescriptor> properties = BEAN_SUPPORT.getProperties(beanClass, 2);
			Map<String, Type> classMap = new HashMap<String, Type>(properties.size());
			Entry<String, PropertyDescriptor> entry;
			for (Iterator<Entry<String, PropertyDescriptor>> it = properties.entrySet().iterator(); it.hasNext();) {
				entry = it.next();
				classMap.put(entry.getKey(), entry.getValue().getReadMethod().getGenericReturnType());
			}
			return classMap;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public boolean isBean() {
		return this.purpose == KSoapConstants.TypePurpose.BEAN;
	}

	public boolean isRequest() {
		return this.purpose == KSoapConstants.TypePurpose.REQUEST;
	}

	public boolean isResponse() {
		return this.purpose == KSoapConstants.TypePurpose.RESPONSE;
	}
	public boolean isFault() {
		return this.purpose == KSoapConstants.TypePurpose.FAULT;
	}
	public boolean isOther() {
		return this.purpose == KSoapConstants.TypePurpose.OTHER;
	}

	/**
	 * @return webServiceInfo
	 */
	public WebServiceInfo getWebServiceInfo() {
		return webServiceInfo;
	}

	/**
	 * @return used
	 */
	public boolean isUsed() {
		return used;
	}

	/**
	 * @param used 要设置的 used
	 */
	public void setUsed(boolean used) {
		this.used = used;
	}

	/**
	 * @return schemaObj
	 */
	public XmlSchemaObject getSchemaObj() {
		return schemaObj;
	}
	/**
	 * @return objectForPort
	 */
	public Object getObjectForPort() {
		return objectForPort;
	}

	/**
	 * @return operation
	 */
	public AxisOperation getOperation() {
		return operation;
	}
	


}
